package org.xiaohanghu.starter;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * @author xiaohanghu
 * */
public class MainStarter {

	private Logger log = Logger.getLogger();

	public void start(StartConfig config) throws IOException,
			InterruptedException, IllegalArgumentException,
			IllegalAccessException, InvocationTargetException,
			NoSuchMethodException, ClassNotFoundException {
		List<String> options = config.getJvmOptions();
		if (null == options || options.isEmpty()) {
			startByCurrentJvm(config);
		} else {
			startByNewJvm(config);
		}
	}

	/**
	 * 启动新的jvm执行main函数
	 * 
	 * @throws IOException
	 * @throws InterruptedException
	 * 
	 * @throws Exception
	 * */
	public void startByNewJvm(StartConfig config) throws IOException,
			InterruptedException {
		log.setLever(config.getLogLever());
		List<String> options = config.getJvmOptions();
		Map<String, String> sysProps = config.getSystemProperties();
		JavaResources javaResources = config.getJavaResources();
		String classPath = null;
		if (null != javaResources) {
			classPath = javaResources.toClassPath();
		}
		String mainClass = config.getMainClass();

		List<String> argsList = config.getArguments();
		String commandLine = null;
		try {
			commandLine = JavaCommandUtils.buildCommandLine(options, sysProps,
					classPath, mainClass, argsList);
		} catch (IOException e) {
			log.error("Build CMD command error! Thorws "
					+ e.getClass().getName() + "!");
			throw e;
		}

		// try {
		try {
			log.info("Start by command:\r\n" + commandLine);
			JavaCommandUtils.runCommand(commandLine);
		} catch (IOException e) {
			log.error("Run command [" + commandLine + "] error! Thorws :"
					+ e.getClass().getName() + "!");
			throw e;
		} catch (InterruptedException e) {
			log.error("Run command [" + commandLine + "] error! Thorws :"
					+ e.getClass().getName() + "!");
			throw e;
		}
	}

	/**
	 * 根据指定配置启动java main
	 * 
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 * @throws Exception
	 * */
	public void startByCurrentJvm(StartConfig config)
			throws IllegalArgumentException, IllegalAccessException,
			InvocationTargetException, NoSuchMethodException,
			ClassNotFoundException {
		log.setLever(config.getLogLever());
		Set<URL> javaResources = config.getJavaResourcesUrls();
		if (javaResources.isEmpty()) {
			log.error("Can't find any java resource!\r\n\tPlease check you \""
					+ StartConfigFactory.PROPERTY_JAR_RESOURCES_NAME
					+ "\" and \""
					+ StartConfigFactory.PROPERTY_CLASSES_RESOURCES_NAME
					+ "\" config.");
			return;
		}

		/** 加载系统参数 */
		Map<String, String> systemProperties = config.getSystemProperties();
		if (null != systemProperties && !systemProperties.isEmpty()) {
			for (Entry<String, String> systemPropertie : systemProperties
					.entrySet()) {
				System.setProperty(systemPropertie.getKey(),
						systemPropertie.getValue());
			}
		}

		/** 加载java资源到ClassLoader */
		ClassLoader classLoader = JavaCodeLoadUtils
				.loadeJavaResourcesByCurrentThread(javaResources);
		StringBuilder stringBuilder = new StringBuilder();
		for (URL javaResource : javaResources) {
			stringBuilder.append("\r\n\t");
			stringBuilder.append(javaResource);
		}
		log.debug("Load java resources : " + stringBuilder.toString());

		/** 启动应用 */
		String mainClass = config.getMainClass();

		try {
			log.info("Start main class : " + mainClass);
			invokeMain(classLoader, mainClass, config.getArguments());
		} catch (ClassNotFoundException e) {
			log.error("ClassNotFoundException! Can't find class by name ["
					+ mainClass + "].");
			throw e;
		} catch (NoSuchMethodException e) {
			log.error("NoSuchMethodException! Can't find main method form class ["
					+ mainClass + "].");
			throw e;
		} catch (SecurityException e) {
			log.error("SecurityException! Can't visit class [" + mainClass
					+ "].", e);
			throw e;
		}
		// catch (Exception e) {
		// log.error("Invoke Main method by class [" + mainClass + "] throws "
		// + e.getClass().getSimpleName() + "!");
		// throw e;
		// }

	}

	private static Class<?>[] MAIN_METHOD_PARAM_TYPES = new Class[] { String[].class };
	private static String MAIN_METHOD_NAME = "main";

	@SuppressWarnings("unchecked")
	public static void invokeMain(ClassLoader classloader, String classname,
			List<String> args) throws ClassNotFoundException,
			SecurityException, NoSuchMethodException, IllegalArgumentException,
			IllegalAccessException, InvocationTargetException {
		Class<?> invoked_class = null;
		invoked_class = classloader.loadClass(classname);
		if (args == null) {
			args = Collections.EMPTY_LIST;
		}
		Method main = invoked_class.getDeclaredMethod(MAIN_METHOD_NAME,
				MAIN_METHOD_PARAM_TYPES);
		String[] argsArray = args.toArray(new String[args.size()]);
		Object[] method_params = new Object[] { argsArray };
		main.invoke(null, method_params);
	}
}
